import base64
import hashlib
import json
import random
import time
import jwt
import requests
from django.views import View
from django.conf import settings
from django.http import JsonResponse
from django.core.cache import caches
from user.models import UserProfile, Address, WeiBoProfile
from .tasks import async_send_active_email, async_send_message
from utils.logging_dec import logging_check
from django.db import transaction
from utils.baseview import BaseView
from utils.sms import YunTongXunAPI
from utils.weiboapi import OAuthWeiAPI
from carts.views import CartsView

CODE_CACHE = caches["default"]
SMS_CACHE = caches["sms"]

# Create your views here.
# CBV 应用场景
'''
    一个路由提供多种Http请求方法时，使用类视图，类视图更符合RESTFUL语义
    优点：
        代码可读性好
        类视图对于函数视图有更高的复用性，项目中其他的功能模块需要用到某个视图功能逻辑时，直接继承该类即可
    使用
    继承django中的view进行方法的转发
        from django.views import view
        class xxxView(View):
            def get()：
    路由匹配
    path("xxxx", views.xxxview.as_view())
    注意：类视图中，如果发送了请求，但是在类视图中未定义方法，则会返回状态玛405
'''


def users(request):
    data = json.loads(request.body)
    uname = data.get("uname")
    password = data.get("password")
    email = data.get("email")
    phone = data.get("phone")
    # 获取短信验证码
    verify = data.get("verify")
    expire_key = f"sms_expire_{verify}"
    redis_code = SMS_CACHE.get(expire_key)
    if not redis_code:
        return JsonResponse({"code": 10108, "error": {"message": "已过期，清重新获取验证码"}})
    if verify != str(redis_code):
        return JsonResponse({"code": 10109, "error": {"message": "验证码有误，请重新是输入"}})

    # 数据库查询
    old_users = UserProfile.objects.filter(username=uname)
    if old_users:
        return JsonResponse({"code": 10100, "error": f"The{uname} is exists!"})
    # 二次开发，try
    try:
        # 发送激活邮件
        verify_url = get_verify_url(uname)
        #发送激活邮件
        async_send_active_email.delay(email, verify_url, uname)
    except Exception as e:
        print("Send Email e----------->", e)
        return JsonResponse({"code": 10100, "error": f"The{uname}的邮件发送失败！！！！！"})
    # 存入数据库
    #密码加密
    m = hashlib.md5()
    m.update(password.encode())
    pwd_md5 = m.hexdigest()
    user = UserProfile.objects.create(username=uname, password=pwd_md5, email=email, phone=phone)
    # 签发token
    token = make_token(uname)
    # 购物车数据合并
    offline_data = data.get("carts")
    carts_count = CartsView().merge_carts(offline_data, user.id)
    # 整理返回数据
    result = {
        "code": 200,
        "username": uname,
        "data": {"token": token},
        "carts_count": carts_count
    }
    return JsonResponse(result)


def active_view(request):
    '''
        邮件激活视图逻辑
        1.获取查询字符串code[request.GET.get("")]
        2.校验code[Redis]
        3.激活用户[is_active=True]
        4.清理Redis中对应的缓存
        5.组织数据返回
        {"code":200, "data":"激活成功"}
    '''
    code = request.GET.get("code")
    if not code:
        return JsonResponse({"code": 10102, "error": "You don't hava code"})
    # 获取明文
    code_str = base64.urlsafe_b64decode(code.encode()).decode()
    number, username = code_str.split("_")
    key = "active_email_%s" % username
    redis_num = CODE_CACHE.get(key)
    if number != redis_num:
        return JsonResponse({"code": 10103, "error": "CODE is wrong!!"})
    try:
        user = UserProfile.objects.get(username=username)
    except Exception as e:
        print("active_view------->", e)
        return JsonResponse({"code": 10104, "error": "Username error!!"})
    user.is_active = True
    user.save()
    # 清理缓存
    CODE_CACHE.delete(key)
    return JsonResponse({"code": 200, "error": "激活成功!!"})


def sms_view(request):
    """
    短信视图
    """
    data = json.loads(request.body)
    phone = data.get("phone")
    # 四位随机数
    code = random.randint(1000, 9999)
    # 先去判断，如果三分钟内发送过短信，则直接返回，否则再发送短信
    key = f"sms_{phone}"
    redis_code = SMS_CACHE.get(key)
    if redis_code:
        # 说明时间线之内发送过
        return JsonResponse({"code": 10107, "error": {"message": "三分钟之内只能发送一次！！"}})
    # 调用短信接口
    async_send_message.delay(phone, code)
    # 控制短信发送频率：存入redis中一份，有效期为三分钟
    SMS_CACHE.set(key, code, 180)
    # 存入Redis控制验证码有效期：有效期十分钟
    expire_key = f"sms_expire_{code}"
    SMS_CACHE.set(expire_key, code, 600)
    return JsonResponse({"code": 200, "data": "发送成功！"})


class DefaultAddressView(BaseView):
    # @logging_check
    def post(self, request, username):
        """设置默认地址视图逻辑
            1.获取请求体参数
            2一查二改三保存
                2.1现地质->设置默认
                2.2原地址设置非默认
        """
        data = request.data
        user = request.myuser
        uid = data.get("id")
        # 开启事务
        with transaction.atomic():
            sid = transaction.savepoint()
            try:
                #将原地址设置为非默认
                old_default = Address.objects.get(is_default=True, user_profile=user)
                old_default.is_default = False
                old_default.save()
                #将现地质设置为默认
                new_default = Address.objects.get(is_default=False, user_profile=user, id=uid)
                new_default.is_default = True
                new_default.save()
            except Exception as e:
                print("设置默认地址失败e--------------------->", e)
                transaction.savepoint_rollback(sid)
                return JsonResponse({"code": 10106, "eroor": "设置默认地址失败"})
            # 提交事务
            transaction.savepoint_commit(sid)
            return JsonResponse({"code": 200, "data": "设置默认地址成功"})


# FBV函数视图CBV类视图
class AddressView(BaseView):

    # @logging_check
    def get(self, request, username):
        '''获取收件地址视图逻辑'''
        user = request.myuser
        all_address = Address.objects.filter(user_profile=user, is_active=True)
        addresslist = []
        for address in all_address:
            address_dict = {
                'id': address.id,
                'address': address.address,
                'receiver': address.receiver,
                'receiver_mobile': address.receiver_mobile,
                'tag': address.tag,
                'postcode': address.postcode,
                'is_default': address.is_default,
            }
            addresslist.append(address_dict)

        return JsonResponse({"code": 200, "addresslist": addresslist})

    # @logging_check
    def post(self, request, username):
        """
        新增地址视图逻辑
            1.获取请求体数据
            2.存入数据表[Address]
                2.1若是第一个地址，新增并设置默认地址
            3.组织数据返回[接口文档]
        """
        data = json.loads(request.body)
        receiver = data.get('receiver')
        receiver_phone = data.get('receiver_phone')
        if len(receiver_phone) > 11:
            return JsonResponse({"code": 104100, "error": "手机号格式不正确"})
        address = data.get('address')
        postcode = data.get('postcode')
        tag = data.get('tag')
        user = request.myuser
        # 处理is_default
        old_address = Address.objects.filter(user_profile=user, is_active=True)
        is_default = False
        if not old_address:
            is_default = True
        # 存入数据
        Address.objects.create(
            user_profile=user,
            receiver=receiver,
            address=address,
            postcode=postcode,
            receiver_mobile=receiver_phone,
            tag=tag,
            is_default=is_default
        )
        return JsonResponse({"code": 200, "data": "新增地址成功！"})

    # @logging_check
    def put(self, request, username, id):
        """
        修改地址视图逻辑
        1.获取请求体数据
        2.查询需要修改的地址
        3.二改 三保存
        4.返回响应
        """
        data = json.loads(request.body)
        address_url = data.get('address')
        add_id = data.get('id')
        receiver_mobile = data.get('receiver_mobile')
        receiver = data.get('receiver')
        tag = data.get('tag')
        user = request.myuser
        try:
            address = Address.objects.get(user_profile=user, id=add_id, is_active=True)
        except Exception as e:
            print("delete address error", e)
            return JsonResponse({'code': 10105, "error": "Get address error!"})
        try:
            address.receiver = receiver
            address.address = address_url
            address.tag = tag
            address.receiver_mobile = receiver_mobile
            address.postcode = tag
        except Exception as e:
            return JsonResponse({'code': 10106, "error": "Message is error!"})
        address.save()
        return JsonResponse({"code": 200, "data": "修改地址成功！"})

    # @logging_check
    def delete(self, request, username, id):
        """
        删除地址视图逻辑
        前提：默认地址不能删除
        1.获取请求体数据
        2.查询该地址
        3.改：判断是否为默认地址
         if add.is_default:
        """
        data = json.loads(request.body)
        add_id = data.get('id')
        user = request.myuser
        try:
            address = Address.objects.get(user_profile=user, id=add_id, is_active=True)
        except Exception as e:
            print("delete address error", e)
            return JsonResponse({'code': 10104, "error": "Get address error!"})
        if address.is_default:
            return JsonResponse({"code": 10105, "error": "默认地址无法删除"})
        # 删除地址
        address.is_active = False
        address.save()
        return JsonResponse({"code": 200, "data": "删除地址成功！"})


class OAuthWeiBoUrlView(View):
    def get(self, request):
        """ 
            获取微博登陆页的视图
            return:{"code"：200, "oauth_url":Xxx}
        """
        weibo_api = OAuthWeiAPI(**settings.WEIBO_CONFIG)
        oauth_url = weibo_api.get_grant_url()
        return JsonResponse({"code": 200, "oauth_url": oauth_url})


class OAuthWeiBoView(View):
    def get(self, request):
        """
            获取access_token视图逻辑
            URl
            方式：POST
            请求体数据：client_id，client_secret，grant_type，code，redirect_uri
        """
        code = request.GET.get("code")
        if not code:
            return JsonResponse({"code": 202, "error": "Not Code"})
        url = 'https://api.weibo.com/oauth2/access_token'
        data = {
            "client_id": settings.WEIBO_CONFIG.get("app_key"),
            "client_secret": settings.WEIBO_CONFIG.get("app_secret"),
            "grant_type": "authorization_code",
            "code": code,
            "redirect_uri": settings.WEIBO_CONFIG.get("redirect_uri")
        }
        access_html = requests.post(url=url, data=data).json()
        """
            {
                'access_token': '2.00IQXvXIVHfkGDf583b08d050zZmHh', 
                'remind_in': '103980', 
                'expires_in': 103980, 
                'uid': '7830394292', 
                'isRealName': 'true'
            }

        """
        # 绑定注册流程
        access_token = access_html.get("access_token")
        uid = access_html.get("uid")
        # 微博表中查询该wuid是否存在
        """
            1.不存在，用户第一次扫码登陆
            2.存在，用户一定扫码登陆过，但不一定注册过
                2.1 已经和正式用户绑定过[200, "username", "token"]
                2.2 没有和正式用户绑定过[201, "uid"]
        """
        try:
            user = WeiBoProfile.objects.get(wuid=uid, access_token=access_token)
        except Exception as e:
            # 出现异常则是第一次扫码登陆
            print("get weibo user error!!!!", e)
            user = WeiBoProfile.objects.create(wuid=uid, access_token=access_token)
            return JsonResponse({"code": 201, "uid": uid})
        user = user.user_profile
        if user:
            return JsonResponse({"code": 200, "username": user.username, "token": make_token(user.username)})
        return JsonResponse({"code": 201, "uid": uid})

    def post(self, request):
        data = json.loads(request.body)
        print(data)
        username = data.get("username")
        password = data.get("password")
        #密码加密
        m = hashlib.md5()
        m.update(password.encode())
        pwd_md5 = m.hexdigest()
        phone = data.get("phone")
        email = data.get("email")
        uid = data.get("uid")
        # 二次开发，try
        try:
            # 发送激活邮件
            verify_url = get_verify_url(username)
            #发送激活邮件
            async_send_active_email.delay(email, verify_url, username)
        except Exception as e:
            print("Send Email e----------->", e)
            return JsonResponse({"code": 10100, "error": f"The{username}的邮件发送失败！！！！！"})
        # 数据库查询
        try:
            user = UserProfile.objects.get(username=username)
            return JsonResponse({"code": 10111, "error": "用户名已存在"})
        except Exception as e:
            with transaction.atomic():
                sid = transaction.savepoint()
                try:
                    user = UserProfile.objects.create(username=username, password=pwd_md5, email=email, phone=phone)
                    # 更新外键
                    print(uid)
                    weibo_user = WeiBoProfile.objects.get(wuid=str(uid))
                    weibo_user.user_profile = user
                    weibo_user.save()
                except Exception as e:
                    print("绑定注册微博失败", e)
                    #回滚
                    transaction.savepoint_rollback(sid)
                    return JsonResponse({"code": 10112, "error": "数据异常，请重试"})
                # 提交事务
                transaction.savepoint_commit(sid)

        token = make_token(username)
        return JsonResponse({"code": 200, "username": username, "token": token})


def get_verify_url(username):
    # 激活链接：http://xxx/active.html?code=xxx
    # :base64(b"1016_zhaoliying")
    code_num = "%d" % random.randint(1000, 9999)
    code = "%s_%s" % (code_num, username)
    code = base64.urlsafe_b64encode(code.encode()).decode()
    # 存储随机数
    key = "active_email_%s" % username
    CODE_CACHE.set(key, code_num, 86400 * 3)
    # 激活连接
    verify_url = "http://127.0.0.1:7000/dadashop/templates/active.html?code=" + code
    return verify_url


def make_token(uname, expire=3600 * 24):
    '''
    生成token
    :param uname:用户名
    ：return :token
    '''
    payload = {
        "exp": int(time.time()) + expire,
        "username": uname
    }
    key = settings.JWT_TOKEN_KEY
    return jwt.encode(payload, key, algorithm="HS256")
